/**
 * 
 */
package com.xoms.front.util.pay.wx;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collection;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;

import org.apache.log4j.Logger;

import com.xoms.front.util.pay.wx.unifiedorder.WXUnifiedOrderRequest;
import com.xoms.util.StringUtil;

/**
 * @author ChengNing
 * @date 2014年12月7日
 */
public class JaxbUtils {

	private static Logger logger = Logger.getLogger(JaxbUtils.class);

	// 多线程安全的Context.
	private JAXBContext jaxbContext;

	/**
	 * @param types
	 *            所有需要序列化的Root对象的类型.
	 */
	public JaxbUtils(Class<?>... types) {
		try {
			jaxbContext = JAXBContext.newInstance(types);
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Java Object->Xml.
	 */
	public String toXml(Object root, String encoding) {
		try {
			StringWriter writer = new StringWriter();
			createMarshaller(encoding).marshal(root, writer);
			return writer.toString();
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Java Object->Xml, 特别支持对Root Element是Collection的情形.
	 */
	@SuppressWarnings("unchecked")
	public String toXml(Collection root, String rootName, String encoding) {
		try {
			CollectionWrapper wrapper = new CollectionWrapper();
			wrapper.collection = root;

			JAXBElement<CollectionWrapper> wrapperElement = new JAXBElement<CollectionWrapper>(new QName(rootName),
					CollectionWrapper.class, wrapper);

			StringWriter writer = new StringWriter();
			createMarshaller(encoding).marshal(wrapperElement, writer);

			return writer.toString();
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Xml->Java Object.
	 */
	@SuppressWarnings("unchecked")
	public <T> T fromXml(String xml) {
		try {
			StringReader reader = new StringReader(xml);
			return (T) createUnmarshaller().unmarshal(reader);
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Xml->Java Object, 支持大小写敏感或不敏感.
	 */
	@SuppressWarnings("unchecked")
	public <T> T fromXml(String xml, boolean caseSensitive) {
		try {
			String fromXml = xml;
			if (!caseSensitive)
				fromXml = xml.toLowerCase();
			StringReader reader = new StringReader(fromXml);
			return (T) createUnmarshaller().unmarshal(reader);
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 创建Marshaller, 设定encoding(可为Null).
	 */
	public Marshaller createMarshaller(String encoding) {
		try {
			Marshaller marshaller = jaxbContext.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			if (!StringUtil.isEmpty((encoding))) {
				marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
			}
			return marshaller;
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 创建UnMarshaller.
	 */
	public Unmarshaller createUnmarshaller() {
		try {
			return jaxbContext.createUnmarshaller();
		} catch (JAXBException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 封装Root Element 是 Collection的情况.
	 */
	public static class CollectionWrapper {
		@SuppressWarnings("unchecked")
		@XmlAnyElement
		protected Collection collection;
	}

	public static void main(String[] args) {
		String xmlStr = "<xml><appid><![CDATA[wxad4d4a886f806629]]></appid>" + "<attach><![CDATA[可啊]]></attach>"
				+ "<bank_type><![CDATA[CFT]]></bank_type>" + "<cash_fee><![CDATA[1]]></cash_fee>"
				+ "<device_info><![CDATA[WEB]]></device_info>" + "<fee_type><![CDATA[CNY]]></fee_type>"
				+ "<is_subscribe><![CDATA[Y]]></is_subscribe>" + "<mch_id><![CDATA[1331099901]]></mch_id>"
				+ "<nonce_str><![CDATA[SUhSavVfhn1463198365073]]></nonce_str>"
				+ "<openid><![CDATA[oY-nYwa0amVRbLrC7_70yyjssNZY]]></openid>"
				+ "<out_trade_no><![CDATA[201605141159253a48224f-56a9-41]]></out_trade_no>"
				+ "<result_code><![CDATA[SUCCESS]]></result_code>" + " <return_code><![CDATA[SUCCESS]]></return_code>"
				+ "<sign><![CDATA[FB5805092713DB131CB5928AB2FA9848]]></sign>"
				+ "<time_end><![CDATA[20160514115929]]></time_end>" + " <total_fee>1</total_fee>"
				+ "<trade_type><![CDATA[JSAPI]]></trade_type>"
				+ "<transaction_id><![CDATA[4000322001201605145836392882]]></transaction_id>" + "</xml>";
		JaxbUtils jabutils = new JaxbUtils(WXUnifiedOrderRequest.class);
		WXUnifiedOrderRequest orderres = jabutils.fromXml(xmlStr);
		System.out.println(orderres);
	}
}
